package it.crosato.stage.server.model.actions;

import it.crosato.stage.server.model.comparison.IInvariantsComparator;
import it.crosato.stage.server.model.comparison.IInvariantsModeller;
import it.crosato.stage.server.model.comparison.ISetsComparator;
import it.crosato.stage.server.model.comparison.ISetsModeller;
import it.crosato.stage.server.model.comparison.InvariantsComparator;
import it.crosato.stage.server.model.comparison.InvariantsModeller;
import it.crosato.stage.server.model.comparison.SetsComparator;
import it.crosato.stage.server.model.comparison.SetsModeller;
import it.crosato.stage.server.model.comparison.Utilities;
import it.crosato.stage.server.model.phylogeny.PhylogeneticTreesBuilder;
import it.crosato.stage.server.model.retriever.IOrganismsListRetriever;
import it.crosato.stage.server.model.retriever.IPathwayRetriever;
import it.crosato.stage.server.model.retriever.IPathwaysListRetriever;
import it.crosato.stage.server.model.retriever.OrganismsListRetriever;
import it.crosato.stage.server.model.retriever.PathwayRetriever;
import it.crosato.stage.server.model.retriever.PathwaysListRetriever;
import it.crosato.stage.shared.exceptions.NonExistentException;
import it.crosato.stage.shared.exceptions.RetrievingException;
import it.crosato.stage.shared.objects.Comparison;
import it.crosato.stage.shared.objects.EntityDefinition;
import it.crosato.stage.shared.objects.Match;
import it.crosato.stage.shared.objects.Pathway;
import it.crosato.stage.shared.objects.SelectionLists;

import java.util.Vector;
import java.util.logging.Logger;

public class Actions implements IActions {
	
	private IOrganismsListRetriever organismsListRetriever = new OrganismsListRetriever();
	private IPathwaysListRetriever pathwaysListRetriever = new PathwaysListRetriever();
	private IPathwayRetriever pathwayRetriever = new PathwayRetriever();
	private ISetsModeller setsModeller = new SetsModeller();
	private ISetsComparator setsComparator = new SetsComparator();
	private IInvariantsModeller invModeller = new InvariantsModeller();
	private IInvariantsComparator invComparator = new InvariantsComparator();
	
	/**
	 * Ritorna le liste degli organismi e delle vie metaboliche disponibili
	 * @param time tempo di scadenza nella banca dati interna
	 * @return l'oggetto contenente le due liste
	 */
	@Override
	public SelectionLists getLists(long time) {
		SelectionLists lists = new SelectionLists();
		lists.setOrganisms(getOrganismsList(time));
		lists.setPathways(getPathwaysList(time));
		return lists;
	}
	
	/**
	 * Effettua il colcolo relativo al confronto e ne ritorna il risultato
	 * @param selectedOrganisms lista degli organismi selezionati
	 * @param selectedPathways lista delle vie metaboliche generali selezionate
	 * @param enzymesSet indica se considerare gli enzimi nel calcolo con gli insiemi
	 * @param enzymesInv indica se considerare gli enzimi nel calcolo con le reti di Petri
	 * @param factor fattore di influenza del modello con le reti di Petri
	 * @param time tempo di scadenza nella banca dati interna
	 * @param sessionId id della sessione, per distinguere i files di 4ti2 di ogni utente
	 * @param applicationPath per memorizzare i files di 4ti2 in una cartella dell'applicazione
	 * @return l'oggetto contenente tutti i dettagli del confronto
	 */
	@Override
	public Comparison compare(Vector<EntityDefinition> selectedOrganisms,
			Vector<EntityDefinition> selectedPathways, boolean enzymesSet, boolean enzymesInv,
			double factor, long time, String sessionId, String applicationPath) {
		
		Comparison comparison = new Comparison();
		
		Vector<String> nonExistentPathways = new Vector<String>();
		comparison.setPathways(getPathways(selectedOrganisms, selectedPathways, nonExistentPathways, time));
		comparison.setNonExistentPathways(nonExistentPathways);
				
		if (comparison.getPathways() != null && nonExistentPathways.size() == 0) {
			
			comparison.setSets(setsModeller.getPathwaysModels(comparison.getPathways(), enzymesSet));
			comparison.setSetDM(setsComparator.getDistancesMatrix(comparison.getSets()));
				
			comparison.setInvariants(invModeller.getPathwaysModels(comparison.getPathways(),
					enzymesInv, sessionId, applicationPath));
			Vector<Match[][]> matches = new Vector<Match[][]>();
			comparison.setInvDM(invComparator.getDistanceMatrix
					(comparison.getInvariants(), matches));
			comparison.setMatches(matches);
			
			comparison.setTotDM(Utilities.calculateDistanceMatrix(comparison.getSetDM(),
					comparison.getInvDM(), factor / 100));
			Vector<String> organisms = new Vector<String>();
			for (int i = 0; i < selectedOrganisms.size(); i++) {
				organisms.add(selectedOrganisms.get(i).getId());
			}
			comparison.setUpgma(PhylogeneticTreesBuilder.UPGMA(comparison.getTotDM(), organisms));
			comparison.setNj(PhylogeneticTreesBuilder.NJ(comparison.getTotDM(), organisms));
		}
		return comparison;
	}
	
	/**
	 * Recupera la lista degli organismi disponibili mediante l'oggetto
	 * OrganismsListRetriever.
	 * @param time tempo di scadenza nella banca dati interna
	 * @return la lista degli organismi disponibili
	 */
	private Vector<EntityDefinition> getOrganismsList(long time) {
		try {
			return organismsListRetriever.getOrganismsList(time);
		}
		catch(RetrievingException re) {
			Logger.getLogger("myLog").info("re ---> " + re.getMessage());
			return null;
		}
	}
	
	/**
	 * Recupera la lista delle vie metaboliche disponibili mediante l'oggetto
	 * PathwaysListRetriever.
	 * @param time tempo di scadenza nella banca dati interna
	 * @return la lista delle vie metaboliche disponibili
	 */
	private Vector<EntityDefinition> getPathwaysList(long time) {
		try {
			return pathwaysListRetriever.getPathwaysList(time);
		}
		catch(RetrievingException re) {
			Logger.getLogger("myLog").info("re ---> " + re.getMessage());
			return null;
		}
	}
	
	/**
	 * Recupera tutte le informazioni delle vie metaboliche selezionate per ogni organismo
	 * selezionato attraverso l'oggetto PathwayRetriever.
	 * @param selectedOrganisms lista degli organismi selezionati
	 * @param selectedPathways lista delle vie metaboliche selezionate
	 * @param nonExistentPathways lista delle vie metaboliche non esistenti sulla quale
	 * fare side-effects
	 * @param time tempo di scadenza nella banca dati interna
	 * @return l'insieme delle vie metaboliche per ogni organismo selezionato con tutte le
	 * informazioni
	 */
	private Pathway[][] getPathways(Vector<EntityDefinition> selectedOrganisms,
			Vector<EntityDefinition> selectedPathways, Vector<String> nonExistentPathways, long time) {
		int nOrganisms = selectedOrganisms.size();
		int nPathways = selectedPathways.size();
		Pathway[][] pathways = new Pathway[nOrganisms][nPathways];
		for (int i = 0; i < nOrganisms; i++) {
			for (int j = 0; j < nPathways; j++) {
				try {
					pathways[i][j] = pathwayRetriever.getPathway(selectedPathways.get(j).getId(),
							selectedOrganisms.get(i).getId(), time);
				}
				catch (RetrievingException rpe) {
					return null;
				}
				catch (NonExistentException nee) {
					nonExistentPathways.add(selectedOrganisms.get(i).getDescription() +
							": " + selectedPathways.get(j).getDescription());
				}
			}
		}
		return pathways;
	}
}
